package com.sparrow.core;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 通过读取Class文件,获得方法形参名称列表
 * 
 * @author wendal(wendal1985@gmail.com)
 * 
 */
public class MethodParameterNames {

	/**
	 * 获取Method的形参名称列表
	 * 
	 * @param method
	 *            需要解析的方法
	 * @return 形参名称列表,如果没有调试信息,将返回null
	 */
	public static List<String> getParamNames(Method method) {
		try {
			int size = method.getParameterTypes().length;
			if (size == 0)
				return new ArrayList<String>(0);
			List<String> list = getParamNames(method.getDeclaringClass()).get(
					getKey(method));
			if (list != null && list.size() != size)
				return list.subList(0, size);
			return list;
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 获取Constructor的形参名称列表
	 * 
	 * @param constructor
	 *            需要解析的构造函数
	 * @return 形参名称列表,如果没有调试信息,将返回null
	 */
	public static List<String> getParamNames(Constructor<?> constructor) {
		try {
			int size = constructor.getParameterTypes().length;
			if (size == 0)
				return new ArrayList<String>(0);
			List<String> list = getParamNames(constructor.getDeclaringClass())
					.get(getKey(constructor));
			if (list != null && list.size() != size)
				return list.subList(0, size);
			return list;
		} catch (Throwable e) {
			throw new RuntimeException(e);
		}
	}

	// ---------------------------------------------------------------------------------------------------

	/**
	 * 获取一个类的所有方法/构造方法的形参名称Map
	 * 
	 * @param klass
	 *            需要解析的类
	 * @return 所有方法/构造方法的形参名称Map
	 * @throws IOException
	 *             如果有任何IO异常,不应该有,如果是本地文件,那100%遇到bug了
	 */
	public static Map<String, List<String>> getParamNames(Class<?> klass)
			throws IOException {
		InputStream in = klass.getResourceAsStream("/"
				+ klass.getName().replace('.', '/') + ".class");
		return getParamNames(in);
	}

	public static Map<String, List<String>> getParamNames(InputStream in)
			throws IOException {
		DataInputStream dis = new DataInputStream(new BufferedInputStream(in));
		Map<String, List<String>> names = new HashMap<String, List<String>>();
		Map<Integer, String> strs = new HashMap<Integer, String>();
		dis.skipBytes(4);// Magic
		dis.skipBytes(2);// 副版本号
		dis.skipBytes(2);// 主版本号

		// 读取常量池
		int constant_pool_count = dis.readUnsignedShort();
		for (int i = 0; i < (constant_pool_count - 1); i++) {
			byte flag = dis.readByte();
			switch (flag) {
			case 7:// CONSTANT_Class:
				dis.skipBytes(2);
				break;
			case 9:// CONSTANT_Fieldref:
			case 10:// CONSTANT_Methodref:
			case 11:// CONSTANT_InterfaceMethodref:
				dis.skipBytes(2);
				dis.skipBytes(2);
				break;
			case 8:// CONSTANT_String:
				dis.skipBytes(2);
				break;
			case 3:// CONSTANT_Integer:
			case 4:// CONSTANT_Float:
				dis.skipBytes(4);
				break;
			case 5:// CONSTANT_Long:
			case 6:// CONSTANT_Double:
				dis.skipBytes(8);
				i++;// 必须跳过一个,这是class文件设计的一个缺陷,历史遗留问题
				break;
			case 12:// CONSTANT_NameAndType:
				dis.skipBytes(2);
				dis.skipBytes(2);
				break;
			case 1:// CONSTANT_Utf8:
				int len = dis.readUnsignedShort();
				byte[] data = new byte[len];
				dis.read(data);
				strs.put(i + 1, new String(data, "UTF-8"));// 必然是UTF8的
				break;
			case 15:// CONSTANT_MethodHandle:
				dis.skipBytes(1);
				dis.skipBytes(2);
				break;
			case 16:// CONSTANT_MethodType:
				dis.skipBytes(2);
				break;
			case 18:// CONSTANT_InvokeDynamic:
				dis.skipBytes(2);
				dis.skipBytes(2);
				break;
			default:
				throw new RuntimeException("Impossible!! flag=" + flag);
			}
		}

		dis.skipBytes(2);// 版本控制符
		dis.skipBytes(2);// 类名
		dis.skipBytes(2);// 超类

		// 跳过接口定义
		int interfaces_count = dis.readUnsignedShort();
		dis.skipBytes(2 * interfaces_count);// 每个接口数据,是2个字节

		// 跳过字段定义
		int fields_count = dis.readUnsignedShort();
		for (int i = 0; i < fields_count; i++) {
			dis.skipBytes(2);
			dis.skipBytes(2);
			dis.skipBytes(2);
			int attributes_count = dis.readUnsignedShort();
			for (int j = 0; j < attributes_count; j++) {
				dis.skipBytes(2);// 跳过访问控制符
				int attribute_length = dis.readInt();
				dis.skipBytes(attribute_length);
			}
		}

		// 开始读取方法
		int methods_count = dis.readUnsignedShort();
		for (int i = 0; i < methods_count; i++) {
			dis.skipBytes(2); // 跳过访问控制符
			String methodName = strs.get(dis.readUnsignedShort());
			String descriptor = strs.get(dis.readUnsignedShort());
			short attributes_count = dis.readShort();
			for (int j = 0; j < attributes_count; j++) {
				String attrName = strs.get(dis.readUnsignedShort());
				int attribute_length = dis.readInt();
				if ("Code".equals(attrName)) { // 形参只在Code属性中
					dis.skipBytes(2);
					dis.skipBytes(2);
					int code_len = dis.readInt();
					dis.skipBytes(code_len); // 跳过具体代码
					int exception_table_length = dis.readUnsignedShort();
					dis.skipBytes(8 * exception_table_length); // 跳过异常表

					int code_attributes_count = dis.readUnsignedShort();
					for (int k = 0; k < code_attributes_count; k++) {
						int str_index = dis.readUnsignedShort();
						String codeAttrName = strs.get(str_index);
						int code_attribute_length = dis.readInt();
						if ("LocalVariableTable".equals(codeAttrName)) {// 形参在LocalVariableTable属性中
							int local_variable_table_length = dis
									.readUnsignedShort();
							List<String> varNames = new ArrayList<String>(
									local_variable_table_length);
							for (int l = 0; l < local_variable_table_length; l++) {
								dis.skipBytes(2);
								dis.skipBytes(2);
								String varName = strs.get(dis
										.readUnsignedShort());
								dis.skipBytes(2);
								dis.skipBytes(2);
								if (!"this".equals(varName)) // 非静态方法,第一个参数是this
									varNames.add(varName);
							}
							names.put(methodName + "," + descriptor, varNames);
						} else
							dis.skipBytes(code_attribute_length);
					}
				} else
					dis.skipBytes(attribute_length);
			}
		}
		dis.close();
		return names;
	}

	/**
	 * 传入Method或Constructor,获取getParamNames方法返回的Map所对应的key
	 */
	public static String getKey(Object obj) {
		StringBuilder sb = new StringBuilder();
		if (obj instanceof Method) {
			sb.append(((Method) obj).getName()).append(',');
			getDescriptor(sb, (Method) obj);
		} else if (obj instanceof Constructor) {
			sb.append("<init>,"); // 只有非静态构造方法才能用有方法参数的,而且通过反射API拿不到静态构造方法
			getDescriptor(sb, (Constructor<?>) obj);
		} else
			throw new RuntimeException("Not Method or Constructor!");
		return sb.toString();
	}

	public static void getDescriptor(StringBuilder sb, Method method) {
		sb.append('(');
		for (Class<?> klass : method.getParameterTypes())
			getDescriptor(sb, klass);
		sb.append(')');
		getDescriptor(sb, method.getReturnType());
	}

	public static void getDescriptor(StringBuilder sb,
			Constructor<?> constructor) {
		sb.append('(');
		for (Class<?> klass : constructor.getParameterTypes())
			getDescriptor(sb, klass);
		sb.append(')');
		sb.append('V');
	}

	/** 本方法来源于ow2的asm库的Type类 */
	public static void getDescriptor(final StringBuilder buf, final Class<?> c) {
		Class<?> d = c;
		while (true) {
			if (d.isPrimitive()) {
				char car;
				if (d == Integer.TYPE) {
					car = 'I';
				} else if (d == Void.TYPE) {
					car = 'V';
				} else if (d == Boolean.TYPE) {
					car = 'Z';
				} else if (d == Byte.TYPE) {
					car = 'B';
				} else if (d == Character.TYPE) {
					car = 'C';
				} else if (d == Short.TYPE) {
					car = 'S';
				} else if (d == Double.TYPE) {
					car = 'D';
				} else if (d == Float.TYPE) {
					car = 'F';
				} else /* if (d == Long.TYPE) */{
					car = 'J';
				}
				buf.append(car);
				return;
			} else if (d.isArray()) {
				buf.append('[');
				d = d.getComponentType();
			} else {
				buf.append('L');
				String name = d.getName();
				int len = name.length();
				for (int i = 0; i < len; ++i) {
					char car = name.charAt(i);
					buf.append(car == '.' ? '/' : car);
				}
				buf.append(';');
				return;
			}
		}
	}
}
